// (C) Copyright 2009, 2010 Communications Programming, Inc.
/***************************************************************************************************************
*  Name: pwm.c
*
*  Description: Function to generate PWM outputs.  
*
*  Calling Parameters:
*  
*	INT16 PWMOpen(	int		ocChan,		// Output Compare Channel #
*					int		timerNum,	// timer number (2 or 3)
*					UINT16	timerMult,	// timer multiplier, eg T2_PS_1_32
*					UINT16	countdownVal );	// timer countdown value
*	
*	timerMult and cowntdownVal together determine the overall period of the PWM:
*
*	For example, if the PIC is running at 80MHz, for countdownVal of 0xFFFF, 
*	timerMult=T2_PS_1_1 gives a period of 820us	
*	timerMult=T2_PS_1_32 gives a period of about 26.25ms
*	timerMult=T2_PS_1_256 gives a period of about 210ms
*	Each of these periods can be shortened by picking a lower countdownVal than the above 0xFFFF, eg
*	timerMult=T2_PS_1_32 with countdown of 0xC000 gives a period of about 20ms
*
*	Returns > 0 handle to use in future i/o calls, 
*				-1 if invalid OC #(must be 1-5), timer # (must be 2 or 3), or mem alloc failed
*
*   INT16 PWMWrite( int handle, UINT16 value );	// writes unsigned 16-bit value to PWM
*
*	value determines the duty cycle.  For example, 
*	value = PR2 would give a 100% duty cycle
*	value = PR2/2 would give a 50% duty cycle
*	value = PR2/4 would give a 25% duty cycle, etc
* 
*	Returns 0 for success, or non-0 error codes in pwm.h if failed
*
*	INT16 PWMClose( UINT16 handle );
*
*	Closes the PWM device
*
*	Returns 0 for success, or non-0 error codes in pwm.h if failed
*
*  Revision History:
*  Date    Who  Comments
*  ------  ---  -----------------------------------------------------------------------------------------
*  101024  RLP	Created.
*
***************************************************************************************************************/

#include <stdlib.h>
#include <plib.h>
//#include <outcompare.h>
#include <userLib.h>
#include <pwm\pwm.h>

static UINT8 timer2Opens = 0;		// non-0 = timer2 is open
static UINT8 timer3Opens = 0;		// non-0 = timer3 is open

INT16 PWMOpen(	int		ocNum,		// Output Compare Channel #SPI Device, eg SPI_CHANNEL1, SPI_CHANNEL1A, etc
				int		timerNum,	// timer number (2 or 3)
				UINT16	timerMult,	// timer multiplier, eg T2_PS_1_32
				UINT16	countdownVal )	// timer countdown value
{
	PWMDevInfo *devInfo;				// device info struct for this device
	UINT16		timerSpec;					// timer setting for OCM open

	// validation: oc number must be 1-5, timer 2-3
	if( ocNum < 1 || ocNum > 5 || timerNum < 2 || timerNum > 3 )
		return(-1);

	if( timerNum == 2 )
		timerSpec = OC_TIMER2_SRC;
	else timerSpec = OC_TIMER3_SRC;
	
	// allocate a device info block

	devInfo = (PWMDevInfo *)malloc( sizeof(PWMDevInfo) );
	if( devInfo == NULL )			// if malloc fails, make sure adequate heap size in Project/Build Options/Project/Linker
		return(-1);
	
	// fill in devInfo block
	
	strcpy( devInfo->devName, "OCMPWM" );
	devInfo->ocNum = ocNum;			// output compare module number
	devInfo->timerNum = timerNum;	// timer number (2 or 3)

	switch( (int)ocNum )
	{
		case 1:
		{
			OpenOC1( OC_ON | timerSpec | OC_PWM_FAULT_PIN_DISABLE, 0, 0);
			break;
		}
		case 2:
		{
			OpenOC2( OC_ON | timerSpec | OC_PWM_FAULT_PIN_DISABLE, 0, 0);
			break;
		}
		case 3:
		{
			OpenOC3( OC_ON | timerSpec | OC_PWM_FAULT_PIN_DISABLE, 0, 0);
			break;
		}
		case 4:
		{
			OpenOC4( OC_ON | timerSpec | OC_PWM_FAULT_PIN_DISABLE, 0, 0);
			break;
		}
		case 5:
		{
			OpenOC5( OC_ON | timerSpec | OC_PWM_FAULT_PIN_DISABLE, 0, 0);
			break;
		}
	}		

	if( timerNum == 2 && !timer2Opens )
	{
		OpenTimer2( T2_ON | timerMult | T2_SOURCE_INT, countdownVal );	
		++timer2Opens;
	}

	else if( timerNum == 3 && !timer3Opens )
	{
		OpenTimer3( T3_ON | timerMult | T3_SOURCE_INT, countdownVal );	
		++timer3Opens;
	}

	return( handleNumberGet((DevInfo *)devInfo) );		// return i/o handle to caller
}

INT16 PWMWrite(INT16 handle, UINT16 value)
{
	PWMDevInfo	 *devInfo;					// device info struct for this device

	// get the device info blk

	devInfo = (PWMDevInfo *)handleDevInfoGet( handle );
	if( devInfo == NULL ) 
		return( PWM_BAD_HANDLE );

	switch( (int)devInfo->ocNum )
	{
		case 1:
		{
			SetDCOC1PWM( value );
			break;
		}
		case 2:
		{
			SetDCOC2PWM( value );
			break;
		}
		case 3:
		{
			SetDCOC3PWM( value );
			break;
		}
		case 4:
		{
			SetDCOC4PWM( value );
			break;
		}
		case 5:
		{
			SetDCOC5PWM( value );
			break;
		}
	}		

	return PWM_SUCCESS;
}

INT16 PWMClose( INT16 handle )
{
	PWMDevInfo *devInfo;					// device info struct for this device

	// get the device info blk

	devInfo = (PWMDevInfo *)handleDevInfoGet( handle );
	if( devInfo == NULL ) 
		return( PWM_BAD_HANDLE );

	switch( (int)devInfo->ocNum )
	{
		case 1:
		{
			CloseOC1();
			break;
		}
		case 2:
		{
			CloseOC2();
			break;
		}
		case 3:
		{
			CloseOC3();
			break;
		}
		case 4:
		{
			CloseOC4();
			break;
		}
		case 5:
		{
			CloseOC5();
			break;
		}
	}		

	// decrement timner open count for timer used, close timer if no more

	if( devInfo->timerNum == 2 )			
	{
		if( timer2Opens )
		{
			if( --timer2Opens == 0 )
				CloseTimer2();
		}
	}
		
	if( devInfo->timerNum == 3 )
	{
		if( timer3Opens )
		{
			if( --timer3Opens == 0 )
				CloseTimer3();
		}
	}
		
	free( (char *)devInfo );				// deallocate dev info block
	handleNumberClose( handle );			// deallocate handle
	return(PWM_SUCCESS);
}
